####################
# Ruyk
# Adding support for OpenMP 3.0


#from yacf.Frontend.C99 import c_parser, c_ast


from yacf.Frontend.Omp.plyparser import PLYParser, Coord, ParseError

from yacf.Frontend.Omp.omp_lexer import OmpLexer

from yacf.Frontend.Omp import omp_ast

# TODO: THIS NODE SHOULD NOT USE PRAGMA node at all
from yacf.Frontend.C99 import c99_ast as c_ast

class OmpParser(PLYParser):

    def p_pp_directive_1(self, p):
       """ pp_directive  : PPHASH pragma_directive 
       """
       p[0] = p[2]

    def p_pragma_directive(self, p):
       """ pragma_directive : PRAGMA OMP_OMP directive"""

       p[0] = [c_ast.Pragma(
            name='omp', 
            stmt = p[3],
            coord=self._coord(p.lineno(1)))]

    def p_pragma_directive_2(self, p):
       """ pragma_directive : PRAGMA LLC llc_directive"""
       p[0] = [c_ast.Pragma(
            name='llc', 
            stmt = p[3],
            coord=self._coord(p.lineno(1)))]

    def p_llc_directive_1(self, p):
       """ llc_directive : NESTED_FOR PPHASH PRAGMA OMP_OMP workshare_directive"""
       p[0] = omp_ast.llcNestedFor(name = 'NESTED FOR', loop = p[5], coord=self._coord(p.lineno(1)))

    def p_llc_directive_2(self, p):
       """ llc_directive : INTERCHANGE iteration_statement"""
       p[0] = omp_ast.llcInterchange(name = 'INTERCHANGE', loop = p[2], coord=self._coord(p.lineno(1)))

    def p_llc_directive_3(self, p):
       """ llc_directive : RESULT LPAREN llc_list RPAREN iteration_statement """
       p[0] = omp_ast.llcResult(name = 'RESULT', result_list = p[3], stmt = p[5], coord=self._coord(p.lineno(1)))

    def p_llc_directive_4(self, p):
       """ llc_directive : UNROLL LPAREN INT_CONST_DEC RPAREN iteration_statement"""
       p[0] = omp_ast.llcUnroll(name = 'UNROLL', factor = p[3], loop = p[5], coord=self._coord(p.lineno(1)))

    def p_llc_directive_5(self, p):
       """ llc_directive : UNSWITCH iteration_statement"""
       p[0] = omp_ast.llcUnswitch(name = 'UNSWITCH', loop = p[2], coord=self._coord(p.lineno(1)))
    
    def p_llc_directive_6(self, p):
       """ llc_directive : TILING LPAREN INT_CONST_DEC RPAREN iteration_statement"""
       p[0] = omp_ast.llcTiling(name = 'TILING', size = p[3], loop = p[5], coord=self._coord(p.lineno(1)))


    # --llc yacf-frangollo ------------------------------
    def p_llc_directive_8(self, p):
        """ llc_directive : REGION llc_clause statement """
        p[0] = omp_ast.llcRegion(name = 'REGION', stmt = p[3], clauses = p[2] or [], coord=self._coord(p.lineno(1)))     

    def p_llc_directive_9(self, p):
        """ llc_directive : TASK llc_clause postfix_expression """
        p[0] = omp_ast.llcTask(name = 'TASK', coord=self._coord(p.lineno(1)))     

    def p_llc_directive_10(self, p):
        """ llc_directive : CONCYCLE """
        p[0] = omp_ast.llcConcycle(name = 'CONCYCLE', coord=self._coord(p.lineno(1)))     

    def p_llc_directive_11(self, p):
        """ llc_directive : workshare_directive """
        p[0] = p[1]

    def p_llc_size_expression(self, p):
        """ llc_size_expression : LBRACKET expression RBRACKET
                                | LBRACKET expression SEMI expression RBRACKET
                                | LBRACKET expression COLON expression RBRACKET
        """
        if p[3] == ':':
            p[0] = omp_ast.llcSizeExpression(start = p[2], end = p[4], size = None)
        else:
            if len(p) > 4:
               p[0] = omp_ast.llcSizeExpression(start = p[2], end = None, size = p[4])
            else:
               p[0] = omp_ast.llcSizeExpression(start = p[2], end = None, size = None)



    def p_llc_complex_identifier_list(self, p):
        """ llc_complex_identifier_list : identifier 
                                        | identifier llc_size_expression
                                        | llc_complex_identifier_list COMMA identifier
                                        | llc_complex_identifier_list COMMA identifier llc_size_expression
        """
        definitions = {}
        if len(p) == 3:
           definitions[p[1]] = p[2]
           p[0] = omp_ast.llcParamList([p[1]], definitions, p[1].coord)
        elif len(p) == 4:
           p[1].params.append(p[3])
           p[0] = p[1]
        elif len(p) == 5:
           definitions[p[3]] = p[4]
           p[1].params.append(p[3])
           p[1].definitions.update(definitions)
           p[0] = p[1]
        else:
            p[0] = omp_ast.llcParamList([p[1]], definitions, p[1].coord)


    def p_llc_clause_1(self, p):
        """ llc_clause : NAME LPAREN STRING_LITERAL RPAREN llc_clause 
                       | COPY_IN  LPAREN llc_complex_identifier_list RPAREN llc_clause
                       | COPY_OUT LPAREN llc_complex_identifier_list RPAREN llc_clause
                       | IN LPAREN identifier_list RPAREN llc_clause
                       | OUT LPAREN identifier_list RPAREN llc_clause
                       | INOUT LPAREN identifier_list RPAREN llc_clause
        """
        tmp = None
        if p[1] == 'name':
            # Remove semicolons fron STRING LITERAL
            tmp = omp_ast.llcName(name = 'NAME', label = p[3][1:-1], coord = self._coord(p.lineno(1)))    
        if p[1] == 'copy_in':
            tmp = omp_ast.llcCopyin(name = 'COPY_IN', identifiers = p[3], coord = self._coord(p.lineno(1)))
        if p[1] == 'copy_out':
            tmp = omp_ast.llcCopyin(name = 'COPY_OUT', identifiers = p[3], coord = self._coord(p.lineno(1)))
        if p[1] == 'in':
            tmp = omp_ast.llcCopyin(name = 'IN', identifiers = p[3], coord = self._coord(p.lineno(1)))
        if p[1] == 'out':
            tmp = omp_ast.llcCopyin(name = 'OUT', identifiers = p[3], coord = self._coord(p.lineno(1)))
        if p[1] == 'inout':
            tmp = omp_ast.llcCopyin(name = 'INOUT', identifiers = p[3], coord = self._coord(p.lineno(1)))
        
        
        p[0] = []
        p[0].append(tmp)
        if p[4]:
            p[0].extend(p[5])

        
    def p_llc_clause_2(self, p):
        """ llc_clause : empty """
        p[0] = []


    def p_llc_list(self, p):
        """ llc_list : AND direct_declarator COMMA INT_CONST_DEC llc_lst """
        p[0] = [omp_ast.llcResultItem(declaration = p[2], size = c_ast.Constant(type = 'int', value = p[4]))] 
        if not p[5] is None:
           p[0].extend(p[5])

    def p_llc_lst_1(self, p):
        """ llc_lst : COMMA llc_list """
        p[0] = p[2]

    def p_llc_lst_2(self, p):
        """ llc_lst : empty """
        p[0] = None

    def p_llc_iteration_statement(self, p):
        """ llc_iteration_statement : PPHASH PRAGMA LLC llc_directive 
                                    | iteration_statement
        """
        if len(p) == 5:
           p[0] = p[4]
        else:
           p[0] = p[1]

    def p_statement_1(self, p):
        """ statement : llc_nc_result """
        p[0] = p[1]

    def p_llc_nc_result(self, p):
       """ llc_nc_result : PPHASH PRAGMA LLC NCRESULT LPAREN llc_nc_list RPAREN statement """
       p[0] = omp_ast.llcNcResult(name = 'NCRESULT', result_list = p[6], stmt = p[8], coord=self._coord(p.lineno(1)))

    def p_llc_nc_list_1(self, p):
        """ llc_nc_list : AND direct_declarator COMMA INT_CONST_DEC COMMA AND direct_declarator  llc_nc_lst """
        p[0] = [omp_ast.llcNcResultItem(declaration = p[2], size = c_ast.Constant(type = 'int', value = p[4])),]
        if not p[8] is None:
            p[0].extend(p[8])

    def p_llc_nc_lst_2(self, p):
        """ llc_nc_lst : COMMA llc_nc_list """
        p[0] = p[2]

    def p_llc_nc_lst_3(self, p):
        """ llc_nc_lst : empty """
        p[0] = None

    def p_workshare_directive_1(self, p):
       """ workshare_directive : OMP_FOR omp_clause_list llc_iteration_statement """
       # Start of a parallel region within a for
       p[0] = omp_ast.OmpFor(name='for', clauses = p[2], stmt = p[3], coord = self._coord(p.lineno(1)))

    def p_workshare_directive_11(self, p):
       """ workshare_directive : OMP_FOR not_omp_clause_list llc_iteration_statement """
       # Start of a parallel region within a for
       p[0] = omp_ast.OmpFor(name='for', clauses = p[2], stmt = p[3], coord = self._coord(p.lineno(1)))


    def p_workshare_directive_2(self, p):
       """ workshare_directive : OMP_FOR  llc_iteration_statement """
     # Start of a parallel region within a for 
       p[0] = omp_ast.OmpFor(name='for', clauses = [], stmt = p[2], coord = self._coord(p.lineno(1)))

    
    def p_workshare_directive_3(self, p):
       """ workshare_directive : SECTIONS omp_clause_list compound_statement """
       # Start of a parallel region within a for
       p[0] = p[3]


    def p_directive_1(self, p):
       """ directive : PARALLEL omp_clause_list compound_statement
       """
       print 'directive1'
       p[0] =  omp_ast.OmpParallel(name='parallel', clauses = p[2], stmt = p[3], coord = self._coord(p.lineno(1)))


    def p_directive_2(self, p):
       """ directive : PARALLEL OMP_FOR omp_clause_list llc_iteration_statement
	                 | PARALLEL OMP_FOR llc_iteration_statement """
       # Start of a parallel region within a for
       if (len(p) == 5):
          p[0] = omp_ast.OmpParallelFor(name='parallel for', clauses = p[3], stmt = p[4], coord = self._coord(p.lineno(1)))
       else:
          p[0] = omp_ast.OmpParallelFor(name='parallel for', clauses = None, stmt = p[3], coord = self._coord(p.lineno(1)))

    def p_directive_3(self,p):
       """ directive : PARALLEL compound_statement """
       # Start of a parallel region
       p[0] = omp_ast.OmpParallel(name='parallel', clauses = None, stmt = p[2], coord = self._coord(p.lineno(1)))

    def p_directive_4(self, p):
       """ directive : workshare_directive """
       # We are on a parallel region
       p[0] = p[1]


    def p_directive_5(self, p):
       """ directive : THREADPRIVATE LPAREN identifier_list RPAREN """
       p[0] = omp_ast.OmpThreadPrivate(name = 'threadprivate', identifiers = p[3], coord = self._coord(p.lineno(1)))


    def p_directive_6(self, p):
       """ directive : omp_sync_directives omp_clause_list 
                     | omp_sync_directives
       """
       if (len(p) == 3):
          p[0] = omp_ast.OmpSyncronization(name = p[1], clauses = p[2], identifiers = None, coord = self._coord(p.lineno(1)))
       elif (len(p) == 2): 
          p[0] = omp_ast.OmpSyncronization(name = p[1], clauses = [], identifiers = None, coord = self._coord(p.lineno(1)))

    def p_directive_7(self, p):
       """ directive : TARGET DEVICE LPAREN device_list RPAREN omp_clause_list
                     | TARGET DEVICE LPAREN device_list RPAREN 
       """
       p[0] = omp_ast.OmpTargetDevice(name = "device", device = p[4], clauses = p[6] if len(p) > 6 else [], coord = self._coord(p.lineno(1)))

    def p_directive_8(self, p):
       """ directive : OMP_FLUSH  LPAREN identifier_list RPAREN 
       """
       p[0] = omp_ast.OmpFlush(name = "flush", identifiers = p[3], coord = self._coord(p.lineno(1)))


    def p_directive_9(self, p):
       """ directive : PARALLEL OMP_FOR omp_clause_list llc_iteration_statement pp_directive"""
      # Start of a parallel region within a for
       p[0] = omp_ast.OmpParallelFor(name='parallel for', clauses = p[3], stmt = p[4], coord = self._coord(p.lineno(1)))

    def p_directive_10(self, p):
       """ directive : PARALLEL OMP_FOR not_omp_clause_list llc_iteration_statement pp_directive"""
      # Start of a parallel region within a for
       p[0] = omp_ast.OmpParallelFor(name='parallel for', clauses = p[3], stmt = p[4], coord = self._coord(p.lineno(1)))


    def p_omp_sync_directives(self,p):
       """ omp_sync_directives : OMP_CRITICAL
                               | OMP_MASTER 
                               | OMP_ATOMIC
                               | OMP_FLUSH
                               | OMP_BARRIER
                               | OMP_SINGLE
       """
       p[0] = p[1]

    def p_omp_clause_list(self, p):
       """ omp_clause_list : clause omp_clause_list
                           | clause """
       if len(p) == 3:
            p[0] = p[1] + p[2]
       else:
            p[0] = p[1]

    def p_not_omp_clause_list(self, p):
        """ not_omp_clause_list : """
        p[0] = []

    def p_clause_1(self, p):
       """ clause : SHARED LPAREN identifier_list RPAREN 
                  | PRIVATE LPAREN identifier_list RPAREN 
                  | COPYIN LPAREN identifier_list RPAREN 
                  | FIRSTPRIVATE LPAREN identifier_list RPAREN
                  | LASTPRIVATE LPAREN identifier_list RPAREN
                  | NUM_THREADS LPAREN constant_expression RPAREN
                  | IF LPAREN constant_expression RPAREN
       """
       p[0] = [omp_ast.OmpClause(type = 'memory', name = str(p[1]).upper(), identifiers = p[3], coord = self._coord(p.lineno(1)))]


    def p_clause_2(self, p):
       """ clause : REDUCTION LPAREN reduction_operator  COLON identifier_list RPAREN """
       p[0] = [omp_ast.OmpClause(type = p[3] , name = 'REDUCTION', identifiers = p[5], coord = self._coord(p.lineno(1)))]

    def p_clause_3(self, p):
       """ clause : NOWAIT """
       p[0] = [omp_ast.OmpClause(type = str(p[1]) , name = str(p[1]).upper(), identifiers = None, coord = self._coord(p.lineno(1)))]



    def p_clause_4(self, p):
       """ clause : SCHEDULE LPAREN OMP_SCHEDULE_KIND RPAREN 
                  | SCHEDULE LPAREN OMP_SCHEDULE_KIND COMMA INT_CONST_DEC RPAREN 
       """
       p[0] = [omp_ast.OmpSchedule(type = str(p[1]) , name = str(p[1]).upper(), chunk_size = p[5] if len(p) > 5 else 0, kind = p[3], coord = self._coord(p.lineno(1)))]



    def p_clause_5(self, p):
       """ clause : COPY_IN LPAREN identifier_list RPAREN """
       p[0] = [omp_ast.OmpClause(type = str(p[1]) , name = str(p[1]).upper(), identifiers = p[3], coord = self._coord(p.lineno(1)))]

    def p_clause_6(self, p):
       """ clause : COPY_OUT LPAREN identifier_list RPAREN """
       p[0] = [omp_ast.OmpClause(type = str(p[1]) , name = str(p[1]).upper(), identifiers = p[3], coord = self._coord(p.lineno(1)))]


    def p_clause_7(self, p):
       """ clause : COLLAPSE LPAREN INT_CONST_DEC RPAREN """
       p[0] = [omp_ast.OmpClause(type = str(p[1]) , name = str(p[1]).upper(), identifiers = None, coord = self._coord(p.lineno(1)))]


    def p_clause_8(self, p):
       """ clause : OMP_DEFAULT LPAREN SHARED RPAREN
                  | OMP_DEFAULT LPAREN NONE RPAREN
       """
       p[0] = [omp_ast.OmpClause(type = p[3] , name = str(p[1]).upper(), identifiers = None, coord = self._coord(p.lineno(1)))]

    def p_clause_9(self, p):
       """ clause : NAME LPAREN STRING_LITERAL RPAREN """
       p[0] = [omp_ast.llcName(name = 'NAME', label = p[3][1:-1], coord = self._coord(p.lineno(1)))]



    def p_device_list(self, p):
       """ device_list  : CUDA
                        | FPGA
                        | CELL 
                        | MPI
                        | SMP
                        | HYBRID """
       p[0] = p[1]



    def p_reduction_operator(self, p):
       """ reduction_operator  : PLUS 
                               | MINUS
                               | TIMES """
       p[0] = p[1]

# End of OpenMP
####################


